package br.com.poc.pitagorasnumero.socialnetwork;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

import roboguice.event.Observes;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import br.com.poc.pitagorasnumero.R;
import br.com.poc.pitagorasnumero.event.BroadcastEventManager;
import br.com.poc.pitagorasnumero.event.FacebookEvent;
import br.com.poc.pitagorasnumero.event.OnActivityResultEvent;
import br.com.poc.pitagorasnumero.util.PitagorasUtil;

import com.facebook.AccessToken;
import com.facebook.AccessTokenSource;
import com.facebook.FacebookException;
import com.facebook.FacebookOperationCanceledException;
import com.facebook.FacebookRequestError;
import com.facebook.Request;
import com.facebook.RequestAsyncTask;
import com.facebook.Response;
import com.facebook.Session;
import com.facebook.SessionLoginBehavior;
import com.facebook.SessionState;
import com.facebook.widget.WebDialog;
import com.facebook.widget.WebDialog.OnCompleteListener;
import com.google.inject.Inject;
import com.google.inject.Singleton;

/**
 * The Class FacebookManager.
 * 
 * @author irlan.gomes
 */
@Singleton
public class FacebookManager {

	/**
	 * Request code to facebook
	 */
	public static final int REQUEST_CODE = Session.DEFAULT_AUTHORIZE_ACTIVITY_CODE;

	/**
	 * Facebook session
	 */
	private Session session;

	/**
	 * Fires events
	 */
	@Inject
	private BroadcastEventManager bem;

	/**
	 * MainActivity context
	 */
	private Context context;

	/**
	 * Retains an instance of SharedPreferences
	 */
	private SharedPreferences sharedPreferences;

	/**
	 * Constant that defines the user_token value.
	 */
	public static final String USER_TOKEN = "user_token";

	/**
	 * Attribute to login status
	 */
	private boolean logged = false;

	private boolean login;

	/**
	 * Keeps a list of given permissions for user account related with
	 * application
	 * 
	 */
	private static final List<String> PERMISSIONS = Arrays
			.asList("publish_actions");

	/**
	 * Listening any change on session status
	 */
	private final Session.StatusCallback sessionCallback = new Session.StatusCallback() {
		@Override
		public void call(final Session session, final SessionState state,
				final Exception exception) {
			onSessionStateChange(session, state);
		}
	};

	/**
	 * Listener when occurs upload of photo on album
	 */
	private final Request.Callback uploadCallback = new Request.Callback() {

		@Override
		public void onCompleted(final Response response) {
			final FacebookRequestError error = response.getError();
			if (error == null) {
				 PitagorasUtil.showToast(context, R.string.upload_completed);
			} else {
				 PitagorasUtil.showToast(context, R.string.upload_error);
			}
		}
	};

	/**
	 * Called to be notified when changes on user session happen: login/logout
	 * 
	 */
	private void onSessionStateChange(final Session session,
			final SessionState state) {
		if (state.isOpened()) {
			saveUserData(session);
			bem.fireEvent(new FacebookEvent(FacebookEvent.LOGGED_IN));
		}
		if (state.isClosed() && login) {
			 bem.fireEvent(new FacebookEvent(FacebookEvent.LOGGED));
			login = false;				
		}
	}

	/**
	 * Init session and do login using user token if have it
	 */
	public void init(final Context context) {

		if (!bem.isInitialized()) {
			bem.onCreate(null);
		}

		this.context = context;

		if (session == null) {
			startNewSession();
		}

		final String userToken = context.getApplicationContext()
				.getSharedPreferences("user_token", 0)
				.getString("user_token", "");

		if (userToken != null && !"".equals(userToken)) {
			loginUsingUserToken(userToken);
		}

	}

	/**
	 * Login using user token store in SharedPreferences
	 * 
	 * @return
	 */
	private void loginUsingUserToken(final String userToken) {

		final AccessToken accessToken = AccessToken
				.createFromExistingAccessToken(userToken,
						session.getExpirationDate(), null,
						AccessTokenSource.FACEBOOK_APPLICATION_WEB, PERMISSIONS);

		session = Session.openActiveSessionWithAccessToken(context,
				accessToken, sessionCallback);

		Session.setActiveSession(session);

		this.logged = true;
	}

	/**
	 * start a new session
	 */
	private void startNewSession() {
		session = new Session.Builder(context).build();
		Session.setActiveSession(session);
	}

	/**
	 * Verify session state and do authentication if needed
	 */
	public void doAuthentication() {

		if (PitagorasUtil.isInternetConnected(context)) {

			if (this.session != null
					&& (Session.getActiveSession().getState()
							.equals(SessionState.CLOSED_LOGIN_FAILED) || Session
							.getActiveSession().getState()
							.equals(SessionState.OPENING))) {

				Session.setActiveSession(null);
				clearSession();
			}

			if (session == null) {
				startNewSession();
			}

			if (!session.isOpened()) {
				final Session.OpenRequest openRequest = new Session.OpenRequest((Activity) context);

				openRequest.setLoginBehavior(SessionLoginBehavior.SUPPRESS_SSO);
				openRequest.setPermissions(Arrays.asList("publish_actions"));
				openRequest.setCallback(sessionCallback);
				session.openForPublish(openRequest);
				login = true;
			}

		} else {
			PitagorasUtil.showToast(context, R.string.fail_connection_internet);
		}
	}

	/**
	 * 
	 */
	private void clearSession() {
		this.session = null;
	}

	/**
	 * Do disconnect from session
	 */
	public void doDisconnect() {
		if (session.isOpened()) {
			cleanUserData();			
			session.closeAndClearTokenInformation();
			session.close();
			clearSession();
			PitagorasUtil.showToast(context, R.string.logout_facebook);
		}
	}

	/**
	 * Share a feed on facebook
	 * 
	 * @param title
	 * @param caption
	 * @param description
	 * @param link
	 * @param pictureUrl
	 */
	public void share(final String title, final String caption,
			final String description, final String link, final String pictureUrl) {

		if (PitagorasUtil.isInternetConnected(context)) {

			if (session.isOpened()) {

				final Bundle postParams = new Bundle();
				postParams.putString("name", title);
				postParams.putString("caption", caption);
				postParams.putString("description", description);
				postParams.putString("link", link);
				postParams.putString("picture", pictureUrl);

				final WebDialog feedDialog = new WebDialog.FeedDialogBuilder(
						context, Session.getActiveSession(), postParams)
						.setOnCompleteListener(new OnCompleteListener() {

							@Override
							public void onComplete(final Bundle values,
									final FacebookException error) {
								if (error == null) {
									PitagorasUtil.showToast(context,
											R.string.number_share_success);

								} else if (error instanceof FacebookOperationCanceledException) {
									PitagorasUtil.showToast(context,
											R.string.number_share_canceled);
								} else {
									PitagorasUtil.showToast(context,
											R.string.number_share_error);
								}
							}

						}).build();

				feedDialog.show();

			} else {
				PitagorasUtil.showToast(context,
						R.string.verify_internet_connection);
			}
		} else {
			PitagorasUtil.showToast(context,
					R.string.verify_internet_connection);
		}
	}

	/**
	 * Saves the user token after login on Facebook
	 * 
	 */
	private void saveUserData(final Session session) {
		final String token = session.getAccessToken();
		this.sharedPreferences = context.getApplicationContext()
				.getSharedPreferences(USER_TOKEN, 0);
		final Editor editorUrls = this.sharedPreferences.edit();
		editorUrls.putString(USER_TOKEN, token);
		editorUrls.commit();
		this.logged = true;
	}

	/**
	 * Cleans the user token after logout from Facebook
	 */
	public void cleanUserData() {
		sharedPreferences = context.getApplicationContext()
				.getSharedPreferences(USER_TOKEN, 0);
		sharedPreferences.edit().remove(USER_TOKEN).commit();
		this.logged = false;
	}

	/**
	 * Observes result of onActivityResult from MainActivity
	 * 
	 * @param event
	 */
	protected void onActivityResult(@Observes final OnActivityResultEvent event) {

		if (event.getRequestCode() == FacebookManager.REQUEST_CODE
				&& event.getResultCode() == Activity.RESULT_OK) {

			session.onActivityResult((Activity) context,
					event.getRequestCode(), event.getResultCode(),
					event.getData());

		} else if (event.getRequestCode() == FacebookManager.REQUEST_CODE
				&& event.getResultCode() == Activity.RESULT_CANCELED) {

			session.close();
			clearSession();
		}
	}

	/**
	 * @return true if logged false if not logged
	 */
	public boolean isLoggedIn() {
		return logged;
	}

	/**
	 * Uploads photo with description on facebook
	 * 
	 * @param description
	 * @param recipeImage
	 */
	public void uploadPhoto(final String description,
			final InputStream recipeImage) {
		if (PitagorasUtil.isInternetConnected(context)) {

			if (session != null) {
				final Request request = Request
						.newUploadPhotoRequest(session,
								BitmapFactory.decodeStream(recipeImage),
								uploadCallback);
				final Bundle params = request.getParameters();
				params.putString("message", description);

				final RequestAsyncTask facebookTask = new RequestAsyncTask(
						request);
				facebookTask.execute();

				PitagorasUtil.showToast(context, R.string.starting_upload);
			}
		} else {
			PitagorasUtil.showToast(context,
					R.string.verify_internet_connection);
		}
	}

	/**
	 * Uploads photo with description on facebook
	 * 
	 * @param description
	 * @param recipeImage
	 */
	public void uploadPhoto(final String description, final File recipeImage)
			throws FileNotFoundException {

		if (PitagorasUtil.isInternetConnected(context)) {

			if (session != null) {
				final Request request = Request.newUploadPhotoRequest(session,
						recipeImage, uploadCallback);
				final Bundle params = request.getParameters();
				params.putString("message", description);

				final RequestAsyncTask facebookTask = new RequestAsyncTask(
						request);
				facebookTask.execute();

				PitagorasUtil.showToast(context, R.string.starting_upload);
			}
		} else {
			PitagorasUtil.showToast(context,
					R.string.verify_internet_connection);
		}
	}

	/**
	 * Unregister receiver to local broadcast
	 */
	public void unregisterReceiver() {
		bem.onDestroy(null);
	}
}
